/**
  ETFDAQ Project
  \class TAv830
  \brief 32 ch ECL input scaler VME module from CAEN Corp. of Italy
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2024-09-02
  \date 2024-09-02 last modified

  \copyright Copyright (c) 2024 IMP-CAS with LGPLv3 LICENSE
*/

#include <bitset>
#include <iostream>
#include "TAv830.h"
#include "TAMsg.h"

#define er TAMsg::Error

using std::cout;
using std::endl;

TAv830::TAv830(TAController *ctrl, const TAYaml &c) : TACAENCard(ctrl, c){
  fCardPurpose = ECardPurpose::SCALER;
  fMAX_EVCTR = 0xffffffff - 100;
} // end ctor

TAv830::~TAv830(){}

void TAv830::Initialize(){
  Config(); // config the card according to user yaml input
  SoftwareReset(); // SR, clear data and most of other config registers
  SlotInit(); // set slot and crate ids
  Identify(); // read card rom for manufacturer info
  // configure interrupt //
  if(fIRQLevel) ConfigIRQ(fIRQLevel, fIRQVector, fEventTrigger);

  // enable or disable channels
  if(fChOn.size() == fNCh){
    std::bitset<64> v;
    for(size_t i = 0; i < fNCh; i++) v.set(i, fChOn[i]);
    SetChOnPattern(v.to_ulong() & 0xffffffff);
  } else SetChOnPattern(0xffffffff); // all on

  RegWriteA32D16(v830reg::ControlBitSet, v830mask::HEADER_ENABLE);
  // berr_enable: expedite blk transfer..
  RegWriteA32D16(v830reg::ControlBitSet, v830mask::BERR_ENABLE);
  RegWriteA32D16(v830reg::ControlBitSet, v830mask::ACQMODELOW); // random trig
  // RegWriteA32D16(v830reg::ControlBitSet, v830mask::AUTORESET);
} // end member function Initialize

// read card info (e.g. serialNo.)
void TAv830::Identify(){
  fCardType = RegReadA32D16(v830rom::BoardIdMSB) << 16 |
    RegReadA32D16(v830rom::BoardId) << 8 | RegReadA32D16(v830rom::BoardIdLSB);
  fSerialNo = RegReadA32D16(v830rom::SerialMSB) << 8 | RegReadA32D16(v830rom::SerialLSB);
  fHardWareRev = RegReadA32D16(v830rom::Revision);
  fFirmWareRev = RegReadA32D16(v830reg::FirmwareRev);
  fVersion = RegReadA32D16(v830rom::Version);
} // end member function Identify

// write slot id and crate id
void TAv830::SlotInit(){
  RegWriteA32D16(v830reg::GeoAddress, fSlot);
} // end member function SlotInit

// enable empty event recording
void TAv830::EnableEmpty() const{
  er("TAv830", "EnableEmpty: not defined for this module type.");
} // end member function EnableEmpty

// level 1~7, 0 will disable the IRQ generation
// vector 0x00~0xFF, requested by vme ctrler to identify the interrupter
// len: trigger will be generated after nof buffered WORDS reaches len
void TAv830::ConfigIRQ(int level, int vector, int len) const{
  RegWriteA32D16(v830reg::InterruptLevel, level & 0x7); // 3 bits
  if(!level) return; // IRQ is disabled
  RegWriteA32D16(v830reg::InterruptVector, vector & 0xFF); // 8 bits
  RegWriteA32D16(v830reg::AlmostFullLevel, len & 0xFFFF); // 16 bits
} // end member function ConfigIRQ

// 32 bits corresponds to ch 0-31 respectively. 1: enable; 0:disable
void TAv830::SetChOnPattern(uint32_t pattern) const{
  RegWriteA32D32(v830reg::ChannelEnable, pattern);
} // end member function SetChOn

// reset counter regs, the MEB, the trig counter, and the MEB ev counter
void TAv830::SoftwareReset(){
  RegWriteA32D16(v830reg::ModuleReset, 0x0); // just write something
} // end member function SoftwareReset

// true if buffered events are enough
bool TAv830::DataReady() const{
  return RegReadA32D16(v830reg::Status) & v830mask::DRDY;
} // end member function DataReady

// \retval nof words read
int TAv830::ReadEvent(uint32_t *buf){
  buf[0] = RegReadA32D32(v830reg::Buffer);
  const int nch = buf[0]>>18 & 0x3F; // nof chs stored in MEB (multi-event buffer)
  // if(fDebug) cout << "nch: " << nch << endl; // DEBUG
  if(!nch) return 1;

  // NOTE that the read pointer has automatically incremented by 1 below
  // (namely, one doesn't have to user v830reg::Buffer+4 in the line below)
  // which is ensured to be done by the hardware mechanism of the card
  // (of course provided that this feature is not deliberately user-disabled)
  const int nget = RegReadA32BLT32(v830reg::Buffer, buf+1, nch) + 1;

  if(fDebug) PrintEvent(buf);
  return nget;
} // end member function ReadEvent

void TAv830::ClearData(){
  // we don't clear v830, we need accumulative counting
  // we only clear v830 for a new run
  if(fEventCounter > fMAX_EVCTR) ClearEventCounter();
} // end member function ClearData

void TAv830::ResetForNewRun(){
  // we only clear v830 for a new run
  ClearEventCounter();
} // end member function ResetForNewRun

// clears the event counter, the counter reg, and the MEB
void TAv830::ClearEventCounter(){
  RegWriteA32D16(v830reg::SoftwareClear, 0x0); // just write something
  // well, anyway the evCtr reg does starts at 1
  fEventCounter = 0; // so fEvCtr should be normally 0 again
} // end member function ClearEventCounter

// return the event counter register
void TAv830::ReadEventCounter(){
  // since the clear data method clears the trigger counter
  // the trigger counter itself is nof ev between adjacent triggers
  // fDNEv = fEventCounter;
  // fEventCounter = RegReadA32D32(v830reg::TriggerCounter);
  // fDNEv = fEventCounter - fDNEv;

  fDNEv = RegReadA32D16(v830reg::MEBEventNumber);
  if(fDebug){
    fEventCounter += fDNEv;
    cout << "\n\033[1m----- Event Counter Print -----\033[0m" << endl;
    TellYouWhoIAm();
    cout << "total: " << fEventCounter;
    cout << ", nof buffered events: " << fDNEv << endl;
    cout << "\033[1m----- BYE -----\033[0m" << endl;
  } // end if
} // end member function ReadEventCounter

// print one event
void TAv830::PrintEvent(const uint32_t *ev){
  static const char *trigSrc[3] = {"external", "trig via timer", "trig via vme"};
  printf("\033[36;1m------ slot %d: %s ------\033[0m\n", fSlot, fCardName.data());
  cout << "\033[32;1m-------- Event Print Begin -------- \033[0m" << endl;
  const uint32_t header = ev[0];
  const int nch = (header>>18 & 0x3F), geo = (header>>27 & 0x1F);
  cout << "Nof Enabled chs: " << nch << ", GEO: " << geo << endl;
  cout << "Trig Source: " << trigSrc[header>>16 & 0x3];
  cout << ", Trig No.: " << (header & 0xFFFF);

  cout << "\n\e[1mEvent Data Print\e[0m";
  for(int i = 0; i < nch; i++){
    if(!(i%4)) cout << endl;
    printf("ch[%02d]: %u\t", i, ev[i+1]);
  } // end for
  cout << "\n\e[1mCounter Print\e[0m";
  for(int i = 0; i < nch; i++){
    if(!(i%4)) cout << endl;
    printf("cntr[%02d]: %u\t", i, RegReadA32D32(v830reg::Counter + i*4));
  } // end for

  cout << "\n\033[32;1m-------- Event Print BYE -------- \033[0m" << endl;
} // end member function PrintEvent

void TAv830::Print() const{
  TACAENCard::Print();
  const uint16_t c = RegReadA32D16(v830reg::Control);
  if((c & 0x3) == 0b01) cout << "Trigger mode: Random, ";
  else if((c & 0x3) == 0b10) cout << "Periodic Trigger" << endl;
  else if((c & 0x3) == 0b00) cout << "Trigger Disabled" << endl;
  if(c & v830mask::HEADER_ENABLE) cout << "Header enabled" << endl;
  else cout << "Header disabled" << endl;
  if(c & v830mask::BERR_ENABLE) cout << "Berr enabled" << endl;
  else cout << "berr disabled" << endl;
  printf("The Control Regstier of %s, slot %d, baseaddr 0x%08x\n",
    fCardName.data(), fSlot, fBaseAddress);
  TAMsg::Print16(RegReadA32D16(v830reg::Control));
  cout << "Channel Enable Register" << endl;
  TAMsg::Print32(RegReadA32D32(v830reg::ChannelEnable));
} // end member function Print
